﻿using System;
using System.Diagnostics;
using System.IO;

namespace PhiStore.Experiments.Compression
{
    class CompressionExperiment : IExperiment
    {
        object critical = new object();
        bool completed = false;
        byte[] bytes;
        int bytesCount = 0;
        int bufferSize = 4096;

        public CompressionExperiment(int bytesCount, bool random, int bufferSize)
        {
            this.bytesCount = bytesCount;
            this.bufferSize = bufferSize;

            bytes = new byte[bytesCount];

            if (random)
            {
                Random rand = new Random();
                rand.NextBytes(bytes);
            }
            else
            {
                for (int i = 0; i < bytesCount; i++)
                {
                    bytes[i] = (byte)i;
                }
            }
        }

        public CompressionExperiment(string filename, int bufferSize)
        {
            this.bufferSize = bufferSize;

            FileStream fs = File.OpenRead(filename);
            bytes = new byte[fs.Length];
            this.bytesCount = bytes.Length;

            fs.Read(bytes, 0, bytesCount);
        }


        public bool IsComplete()
        {
            lock (critical)
            {
                return completed;
            }
        }

        public string getResults()
        {
            if (!IsComplete())
            {
                return "";
            }
            else
            {
                double mb = ((double)bytesCount) / 1024.0 / 1024.0;

                string result = bytesCount + " bytes compressed\r\n";
                result += "GZip compress time: " + GZipCompressTime + " ms (" + (mb / (GZipCompressTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                result += "GZip decompress time: " + GZipDecompressTime + " ms (" + (mb / (GZipDecompressTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                result += "GZip compress size: " + GZipCompressLength + " bytes (" + (GZipCompressLength / bytesCount).ToString("P") + ")\r\n";
                result += "Zip compress time: " + ZipCompressTime + " ms (" + (mb / (ZipCompressTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                result += "Zip decompress time: " + ZipDecompressTime + " ms (" +( mb / (ZipDecompressTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                result += "Zip compress size: " + ZipCompressLength + " bytes (" + (ZipCompressLength / bytesCount).ToString("P") + ")\r\n";
                result += "Build-in GZip compress time: " + buildGZipCompressTime + " ms (" + (mb / (buildGZipCompressTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                result += "Build-in GZip decompress time: " + buildGZipDecompressTime + " ms (" + (mb / (buildGZipDecompressTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                result += "Build-in GZip compress size: " + buildGZipCompressLength + " bytes (" + (buildGZipCompressLength / bytesCount).ToString("P") + ")\r\n";
                result += "Deflate compress time: " + deflateCompressTime + " ms (" + (mb / (deflateCompressTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                result += "Deflate decompress time: " + deflateDecompressTime + " ms (" + (mb / (deflateDecompressTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                result += "Deflate compress size: " + deflateCompressLength + " bytes (" + (deflateCompressLength / bytesCount).ToString("P") + ")\r\n";
                result += "Basic Copy: " + BasicCopyTime + " ms (" + (mb / (BasicCopyTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                result += "Stream Copy: " + StreamCopyTime + " ms (" + (mb / (StreamCopyTime / 1000)).ToString("0.##") + " MB/s)\r\n";
                return result;
            }
        }

        double GZipCompressTime = 0;
        double GZipDecompressTime = 0;
        double GZipCompressLength = 0;

        double ZipCompressTime = 0;
        double ZipDecompressTime = 0;
        double ZipCompressLength = 0;


        double buildGZipCompressTime = 0;
        double buildGZipDecompressTime = 0;
        double buildGZipCompressLength = 0;

        double deflateCompressTime = 0;
        double deflateDecompressTime = 0;
        double deflateCompressLength = 0;

        double BasicCopyTime = 0;
        double StreamCopyTime = 0;


        public void Run()
        {
            Stopwatch watch = new Stopwatch();
            #region Gzip
            
            watch.Start();

            Stream compressed = GZ.Compress(bytes, bufferSize);
            watch.Stop();

            GZipCompressTime = watch.ElapsedMilliseconds;
            compressed.Position = 0;
            GZipCompressLength = compressed.Length;

            watch.Restart();
            Stream decompressed = GZ.Decompress(compressed, bufferSize);
            watch.Stop();

            GZipDecompressTime = watch.ElapsedMilliseconds;

            #endregion
           
            compressed.Position = 0;
            compressed.SetLength(0L);
            decompressed.Position = 0;
            decompressed.SetLength(0L);

            #region zip
            watch.Restart();

            compressed = Zip.Compress(bytes, bufferSize);
            watch.Stop();

            ZipCompressTime = watch.ElapsedMilliseconds;
            compressed.Position = 0;
            ZipCompressLength = compressed.Length;

            watch.Restart();
            decompressed = Zip.Decompress(compressed, bufferSize);
            watch.Stop();

            ZipDecompressTime = watch.ElapsedMilliseconds;
            #endregion

            #region build in gzip

            watch.Restart();
            compressed = BuildInGZip.Compress(bytes, bufferSize);
            watch.Stop();

            buildGZipCompressTime = watch.ElapsedMilliseconds;
            compressed.Position = 0;
            buildGZipCompressLength = compressed.Length;

            watch.Restart();
            decompressed = BuildInGZip.Decompress(compressed, bufferSize);
            watch.Stop();

            buildGZipDecompressTime = watch.ElapsedMilliseconds;

            #endregion

            #region deflate
            watch.Restart();
            compressed = Deflate.Compress(bytes, bufferSize);
            watch.Stop();

            deflateCompressTime = watch.ElapsedMilliseconds;
            compressed.Position = 0;
            deflateCompressLength = compressed.Length;

            watch.Restart();
            decompressed = Deflate.Decompress(compressed, bufferSize);
            watch.Stop();

            deflateDecompressTime = watch.ElapsedMilliseconds;
            #endregion

            #region Basic Copy
            watch.Restart();
            Stream Copy = BasicCopy.BasicStreamCopy(bytes, bufferSize);
            watch.Stop();
            BasicCopyTime = watch.ElapsedMilliseconds;

            watch.Restart();
            Copy = BasicCopy.StreamToStreamCopy(Copy, bufferSize);
            watch.Stop();
            StreamCopyTime = watch.ElapsedMilliseconds;
            #endregion


            lock (critical)
            {
                completed = true;
            }

        }
    }
}
